package org.dslforge.aurum;

public final class Symbol {
	static final Symbol EPSILON = terminal("$epsilon");

	static final Symbol EOF = terminal("$eof");

	public static Symbol nonterminal(String identifier) {
		return new Symbol(identifier, true);
	}

	public static Symbol terminal(String identifier) {
		return new Symbol(identifier, false);
	}

	final String identifier;

	final boolean isNonterminal;

	Symbol(String identifier, boolean isNonterminal) {
		this.identifier = identifier;
		this.isNonterminal = isNonterminal;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null)
			return false;
		if (obj.getClass() != getClass())
			return false;
		Symbol symbol = (Symbol) obj;
		return identifier.equals(symbol.identifier) && isNonterminal == symbol.isNonterminal;
	}

	@Override
	public int hashCode() {
		return identifier.hashCode();
	}

	@Override
	public String toString() {
		return identifier;
	}
}

class Production {
	final Symbol nonterminal;

	final Symbol[] symbols;

	Production(Symbol nonterminal, Symbol... symbols) {
		this.nonterminal = nonterminal;
		this.symbols = symbols;
	}

	Symbol get(int position) {
		if (symbols.length == 0 && position == 0)
			return Symbol.EPSILON;
		return position < symbols.length ? symbols[position] : null;
	}

	Symbol leftEnd() {
		return get(0);
	}

	Symbol rightEnd() {
		return symbols.length == 0 ? get(0) : get(symbols.length - 1);
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == this)
			return true;
		if (obj == null || obj.getClass() != getClass())
			return false;
		Production production = (Production) obj;
		if (!production.nonterminal.equals(nonterminal)
				|| production.symbols.length != symbols.length)
			return false;
		for (int i = 0; i < symbols.length; i++)
			if (!production.symbols[i].equals(symbols[i]))
				return false;
		return true;
	}

	@Override
	public int hashCode() {
		int hashCode = 0;
		for (Symbol symbol : symbols)
			hashCode = hashCode * 37 + symbol.hashCode();
		return hashCode;
	}
}
